package com.tengju.bff.infrastructure.config;

import com.google.common.collect.Lists;
import com.tengju.bff.interfaces.shared.ClassUtil;
import com.tengju.bff.interfaces.shared.servlet.*;
import com.tengju.bff.interfaces.shared.servlet.realm.JwtRealm;
import com.tengju.bff.interfaces.shared.servlet.realm.SsoRealm;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.map.HashedMap;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.mgt.DefaultWebSubjectFactory;
import org.springframework.aop.framework.autoproxy.DefaultAdvisorAutoProxyCreator;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.bind.annotation.RequestMapping;

import javax.servlet.Filter;
import java.lang.reflect.Method;
import java.util.*;

@Slf4j
@Configuration
public class ShiroConfig {


    @Value("${header.needLog}")
    Boolean needLogHeader;

    @Value("${system.env}")
    String env;

    @Bean(name = "jwtRealm")
    public JwtRealm jwtRealm() {
        JwtRealm realm = new JwtRealm();
        realm.setCachingEnabled(true);
        return realm;
    }

    @Bean(name = "ssoRealm")
    public SsoRealm ssoRealm() {
        SsoRealm realm = new SsoRealm();
        realm.setCachingEnabled(true);
        return realm;
    }

    @Bean(name = "subjectFactory")
    public DefaultWebSubjectFactory subjectFactory() {
        DefaultWebSubjectFactory statelessDefaultSubjectFactory = new DefaultWebSubjectFactory();
        return statelessDefaultSubjectFactory;
    }


    @Bean(name = "securityManager")
    public SecurityManager securityManager(@Qualifier("jwtRealm") JwtRealm jwtRealm, @Qualifier("ssoRealm") SsoRealm ssoRealm, @Qualifier("subjectFactory") DefaultWebSubjectFactory subjectFactory) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        Collection<Realm> realms = Lists.newArrayList(jwtRealm, ssoRealm);
        securityManager.setRealms(realms);
        securityManager.setSubjectFactory(subjectFactory);
        return securityManager;
    }

//    @Bean(name = "jwtFilter")
//    public JwtFilter jwtFilter() {
//        return new JwtFilter();
//    }

//    @Bean
//    public FilterRegistrationBean delegatingFilterProxy() {
//        FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean();
//        DelegatingFilterProxy proxy = new DelegatingFilterProxy();
//        proxy.setTargetFilterLifecycle(true);
//        proxy.setTargetBeanName("shiroFilter");
//        filterRegistrationBean.setFilter(proxy);
////        filterRegistrationBean.addUrlPatterns("/*");//过滤所有路径
////        filterRegistrationBean.setOrder(1);//优先级，最顶级
//        return filterRegistrationBean;
//    }

    @Bean(name = "shiroFilter")
    public ShiroFilterFactoryBean shiroFilter(@Qualifier("securityManager") SecurityManager securityManager) {
        try {
            ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
            shiroFilterFactoryBean.setSecurityManager(securityManager);
            //
            Map<String, Filter> filters = new HashedMap(2);
            filters.put("jwtFilter", new JwtFilter(needLogHeader));
            filters.put("anonFilter", new AnonFilter());
            shiroFilterFactoryBean.setFilters(filters);
            //拦截链
            Map<String, String> filterChainDefinitionMap = new LinkedHashMap();

            //过滤swagger
            filterChainDefinitionMap.put("/swagger-ui.html", "anon");
            filterChainDefinitionMap.put("/webjars/**", "anon");
            filterChainDefinitionMap.put("/v2/**", "anon");
            filterChainDefinitionMap.put("/swagger-resources/**", "anon");
            filterChainDefinitionMap.put("/", "anon");
            filterChainDefinitionMap.put("/csrf", "anon");
            filterChainDefinitionMap.put("/tengju/status.ok", "anon");
            filterChainDefinitionMap.put("/status.ok", "anon");
            filterChainDefinitionMap.put("/favicon.ico", "anon");

            List<String> scanPack = List.of("com.tengju.bff.interfaces.app",
                    "com.tengju.bff.interfaces.data",
                    "com.tengju.bff.interfaces.login",
                    "com.tengju.bff.interfaces.manage",
                    "com.tengju.bff.interfaces.openapi",
                    "com.tengju.bff.interfaces.covercharge.controller",
                    "com.tengju.bff.interfaces.test",
                    "com.tengju.bff.interfaces.temp",
                    "com.tengju.support.interfaces"
                    );
            Set<Class<?>> classes = ClassUtil.getClassesByPackList(scanPack);

            for (Class clz : classes) {
                RequestMapping clzRequestMapping = (RequestMapping) clz.getAnnotation(RequestMapping.class);
                if (clzRequestMapping == null) {
                    continue;
                }
                ManageEntrance manageEntrance = (ManageEntrance) clz.getAnnotation(ManageEntrance.class);
                String preUrl = "";
                String packageName = clz.getPackageName();

                String classUrl;
                if (!clzRequestMapping.value()[0].endsWith("/")) {
                    classUrl = clzRequestMapping.value()[0] + "/";
                } else {
                    classUrl = clzRequestMapping.value()[0];
                }

                if (manageEntrance != null) {
                    preUrl = "/boss/" + classUrl;
                } else if (packageName.startsWith("com.tengju.bff.interfaces.app") ||
                        packageName.startsWith("com.tengju.bff.interfaces.login") ||
                        packageName.startsWith("com.tengju.bff.interfaces.data")) {
                    preUrl = "/api/" + classUrl;
                } else {
                    preUrl = classUrl;
                }


                Method[] methods = clz.getDeclaredMethods();
                for (Method method : methods) {
                    NotTokenValid annotation = method.getAnnotation(NotTokenValid.class);
                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);

                    Env envEnum = Env.fromVal(env);

                    if (annotation != null && Arrays.asList(annotation.env()).contains(envEnum)) {
                        String url;
                        if (requestMapping.value()[0].startsWith("/")) {
                            url = preUrl + requestMapping.value()[0].substring(1);
                        } else {
                            url = preUrl + requestMapping.value()[0];
                        }
                        filterChainDefinitionMap.put(url, "anonFilter");
                    }

                }

            }

            filterChainDefinitionMap.put("/**", "jwtFilter");
            shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
            return shiroFilterFactoryBean;
        } catch (Exception e) {
            log.error("shiroFilter create error:",e);
            throw e;
        }

    }

    /**
     * 使用shiro注解配置
     */
    @Bean(name = "advisorAutoProxyCreator")
    public DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator() {
        DefaultAdvisorAutoProxyCreator advisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();
        advisorAutoProxyCreator.setProxyTargetClass(true);
        return advisorAutoProxyCreator;
    }

    @Bean(name = "authorizationAttributeSourceAdvisor")
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }
}
